package com.lbj.mall.product.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lbj.mall.common.utils.PageUtils;
import com.lbj.mall.common.utils.Query;
import com.lbj.mall.product.dao.CategoryDao;
import com.lbj.mall.product.entity.CategoryEntity;
import com.lbj.mall.product.entity.vo.Catalog2Vo;
import com.lbj.mall.product.service.CategoryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {

    @Autowired
    private StringRedisTemplate redis;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public List<CategoryEntity> queryListTree() {
        // 查询所有数据
        // baseMapper = new CategoryDao()
        // CategoryDao实现BaseMapper接口
        // ServiceImpl 中的baseMapper就是指向 new CategoryDao()
        List<CategoryEntity> allList = baseMapper.selectList(null);

        // 返回的就是三级目录结构数据
        return allList.stream().filter(item -> {
                    // filter过滤：将满足条件的item保留下来
                    // CategoryEntity中parentCid字段为0表示是一级目录
                    return item.getParentCid() == 0;
                }).peek(item -> {
                    // 为一级目录设置二级目录
                    item.setChildren(getCategoryChildren(item, allList));
                })
                // 设置按照 sort 属性进行排序
                .sorted(Comparator.comparingInt(item -> item.getSort() == null ? 0 : item.getSort()))
                .collect(Collectors.toList());
    }

    /**
     * 逻辑删除
     *
     * @param asList 要删除数据的id
     */
    @Override
    public int removeMenuByIds(List<Long> asList) {
        // TODO 1.这里需要判断当前的类别是否在别处引用，如果被引用则不能删除
        return baseMapper.deleteBatchIds(asList);
    }

    @Cacheable(value = "product", key = "#root.methodName") // 添加缓存到product分区，会将返回值的结果缓存，再次请求则不会调用方法而是从缓存中获取
    @Override
    public List<CategoryEntity> queryLevelIs1() {
        log.debug("index.....");
        return baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
    }

    @Override
    public Map<String, List<Catalog2Vo>> getCatalog() {
        // 从redis中获取数据
        String categoryJSON = redis.opsForValue().get("categoryJSON");
        // 如果redis中没有数据，则从数据库中获取
        if (StringUtils.isEmpty(categoryJSON)) {
            Map<String, List<Catalog2Vo>> result = getCatalogFromDB();
            // 将数据保存到缓存中，方便下次获取
            // 添加过期时间：避免缓存雪崩问题
            // 添加null信息的保存：避免缓存穿透的问题
            redis.opsForValue().set("categoryJSON", JSON.toJSONString(result), 1, TimeUnit.DAYS);
            return result;
        }
        // 因为是JSON数据，需要转换成对应的实体类
        // 因为是复杂类型，所以需要传递参数
        return JSON.parseObject(categoryJSON, new TypeReference<>() {
        });
    }

    /**
     * 加锁：防止缓存击穿问题
     */
    public synchronized Map<String, List<Catalog2Vo>> getCatalogFromDB() {
        // 加锁完毕：需要再次查看缓存中是否有已有的数据，避免再次查询
        String categoryJSON = redis.opsForValue().get("categoryJSON");
        if (!StringUtils.isEmpty(categoryJSON)) {
            return JSON.parseObject(categoryJSON, new TypeReference<>() {
            });
        }

        // 调用已写的方法
        List<CategoryEntity> categoryEntities = queryListTree();
        // Collectors.toMap 表示将数据收集成Map
        Map<String, List<Catalog2Vo>> collect = categoryEntities.stream().collect(Collectors.toMap(
                k -> k.getCatId().toString(),
                child1 -> child1.getChildren().stream().map(child2 -> {
                    Catalog2Vo child2Vo = new Catalog2Vo();
                    List<Catalog2Vo.Catalog3Vo> child3List = child2.getChildren().stream()
                            .map(child3 ->
                                    new Catalog2Vo.Catalog3Vo(child2.getCatId().toString(),
                                            child3.getCatId().toString(), child3.getName()))
                            .collect(Collectors.toList());
                    child2Vo.setCatalog1Id(child1.getCatId().toString());
                    child2Vo.setId(child2.getCatId().toString());
                    child2Vo.setName(child2.getName());
                    child2Vo.setCatalog3List(child3List);
                    return child2Vo;
                }).collect(Collectors.toList())));

        // 解决加锁之后查询数据库会超过1次
        // 就是在释放锁之前将数据保存到缓存中
        redis.opsForValue().set("categoryJSON", JSON.toJSONString(collect), 1, TimeUnit.DAYS);

        return collect;
    }

    /**
     * 设置当前类的所有下级元素
     *
     * @param current 当前对象
     * @param total   子类对象集合
     * @return 返回所有子类集合
     */
    private List<CategoryEntity> getCategoryChildren(CategoryEntity current, List<CategoryEntity> total) {
        return total.stream()
                // 父类对象的 cid 等于子类对象的 parent_cid 属性
                .filter(item -> current.getCatId().equals(item.getParentCid()))
                .peek(item -> item.setChildren(getCategoryChildren(item, total)))
                .sorted(Comparator.comparingInt(item -> item.getSort() == null ? 0 : item.getSort()))
                .collect(Collectors.toList());
    }
}